package com.piece.core.web.aspect;

import com.piece.core.framework.annotation.source.Lock;
import com.piece.core.framework.exception.LockException;
import com.piece.core.framework.util.basic.SpringExpressionUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.lock.DistributedLock;
import com.piece.core.framework.util.lock.ZLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

/**
 * 分布式锁切面
 */
@Slf4j
@Aspect
@Order(10)
public class LockAspect {

    @Autowired(required = false)
    private DistributedLock locker;

    @Around("@within(lock) || @annotation(lock)")
    public Object aroundLock(ProceedingJoinPoint point, Lock lock) throws Throwable {
        if (null == lock) {
            // 获取类上的注解
            lock = point.getTarget().getClass().getDeclaredAnnotation(Lock.class);
        }
        String lockKey = lock.key();
        if (null == locker) {
            throw new LockException("DistributedLock is null");
        }
        if (StringUtil.isEmpty(lockKey)) {
            throw new LockException("lockKey is null");
        }

        if (lockKey.contains("#")) {
            Object result = SpringExpressionUtil.parseExpression(point, lockKey);
            lockKey = JsonUtil.toJson(result);
        }
        ZLock lockObj = null;
        try {
            //加锁
            if (lock.waitTime() > 0) {
                lockObj = locker.tryLock(lockKey, lock.waitTime(), lock.leaseTime(), lock.unit(), lock.isFair());
            } else {
                lockObj = locker.lock(lockKey, lock.leaseTime(), lock.unit(), lock.isFair());
            }

            if (null != lockObj) {
                return point.proceed();
            } else {
                throw new LockException();
            }
        } finally {
            locker.unlock(lockObj);
        }
    }
}
